
#include "D3d11Utils.h"
#include "D3DX11tex.h"

#include "Mesh.h"
#include "ResourceManager.h"

#include <string>

Mesh::Mesh()
{
	mpMeshParts = NULL;
}

Mesh* Mesh::Create(const MeshData *pMeshData, const DeviceManager* pDeviceManager)
{
	HRESULT hr = S_OK;

	Mesh* pMesh= new Mesh();

	pMesh->mpDeviceManager = pDeviceManager;

	pMesh->mpIndexBuffer = NULL;
	pMesh->mpVertexBuffer = NULL;

	pMesh->mVertexCount = pMeshData->mVertexCount;
	
	D3D11_BUFFER_DESC bd;
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof(*pMeshData->mpVertices) * pMesh->mVertexCount;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	D3D11_SUBRESOURCE_DATA initData = D3D11_SUBRESOURCE_DATA();
	initData.pSysMem = pMeshData->mpVertices;
	HR(hr = pDeviceManager->mpDevice->CreateBuffer( &bd, &initData, &pMesh->mpVertexBuffer ));
	if(hr != S_OK)
	{	
		pMesh->Release();
		delete pMesh;
		return NULL;
	}
	
	pMesh->mIndexCount = pMeshData->mIndexCount;
	D3D11_BUFFER_DESC ibd;
	ibd.Usage = D3D11_USAGE_DEFAULT;
	ibd.ByteWidth = sizeof(*pMeshData->mpIndices) * pMesh->mIndexCount;
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	ibd.CPUAccessFlags = 0;
	ibd.MiscFlags = 0;
	D3D11_SUBRESOURCE_DATA initIndexData = D3D11_SUBRESOURCE_DATA();
	initIndexData.pSysMem = pMeshData->mpIndices;
	
	HR (hr = pDeviceManager->mpDevice->CreateBuffer( &ibd, &initIndexData, &pMesh->mpIndexBuffer ) );
	
	if(hr!=S_OK)
	{
		pMesh->Release();
		delete pMesh;
		return NULL;
	}

	pMesh->mMeshCount = pMeshData->mSubsetCount;
	pMesh->mpMeshParts = new MeshPart[pMesh->mMeshCount];

	SubsetData* subset;
	MeshPart meshPart;

	for(int i=0; i < pMesh->mMeshCount; i++)
	{
		subset = &pMeshData->mpSubsets[i];

		meshPart = MeshPart();

		meshPart.mpDiffuseMap = 0;
		meshPart.mpNormalMap = 0;
		meshPart.mStartIndex = subset->mStartIndex;
		meshPart.mIndexCount = subset->mTriangleCount * 3;
		meshPart.mDiffuse =  XMLoadFloat4(&subset->mDiffuse);
		meshPart.mSpecular = XMLoadFloat4(&subset->mSpecular);
		meshPart.mShininess = subset->mShininess;
		
		if(subset->mDiffuseMapFilePath.length()!=0)
		{
			meshPart.mpDiffuseMap = ResourceManager::GetInst()->CreateShaderResourceViewFromFile(subset->mDiffuseMapFilePath.c_str());
		}

		if(subset->mNormalMapFilePath.length()!=0)
		{
			meshPart.mpNormalMap = ResourceManager::GetInst()->CreateShaderResourceViewFromFile(subset->mNormalMapFilePath.c_str());
		}

		pMesh->mpMeshParts[i] = meshPart;
	}

	return pMesh;
}

void Mesh::Release()
{
	SAFE_RELEASE(mpVertexBuffer);
	SAFE_RELEASE(mpIndexBuffer);
	
	for(int i=0; i < mMeshCount; i++)
	{
		SAFE_RELEASE(mpMeshParts[i].mpDiffuseMap);
		SAFE_RELEASE(mpMeshParts[i].mpNormalMap);
	}

	if(mpMeshParts!=NULL)
	{
		delete[] mpMeshParts;
	}
}

int Mesh::GetVertexCount() const
{
	return mVertexCount;
}

int Mesh::GetIndexCount() const
{
	return mIndexCount;
}

int Mesh::GetMeshCount() const
{
	return mMeshCount;
}

MeshPart* Mesh::GetMeshPart(int index) const
{
	return &mpMeshParts[index];
}

// Private
inline void Mesh::PrepareDraw() const
{
	mpDeviceManager->mpContext->IASetIndexBuffer(mpIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
	UINT stride = sizeof(Vertex);
	UINT offset = 0;
	ID3D11Buffer* buffer[1]={mpVertexBuffer};
	mpDeviceManager->mpContext->IASetInputLayout(mpInputLayout);
	mpDeviceManager->mpContext->IASetVertexBuffers(0, 1, buffer, &stride, &offset);
}

void Mesh::Draw(int index) const
{
	PrepareDraw();
	mpDeviceManager->mpContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	mpDeviceManager->mpContext->DrawIndexed( mpMeshParts[index].mIndexCount, mpMeshParts[index].mStartIndex, 0);
}

void Mesh::Draw() const
{
	PrepareDraw();	
	mpDeviceManager->mpContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	mpDeviceManager->mpContext->DrawIndexed(mIndexCount,0,0);
}


void Mesh::DrawPatch(int index) const
{	
	PrepareDraw();
	mpDeviceManager->mpContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);	
	mpDeviceManager->mpContext->DrawIndexed(mpMeshParts[index].mIndexCount, mpMeshParts[index].mStartIndex, 0);
}

void Mesh::DrawPatch() const
{
	PrepareDraw();
	mpDeviceManager->mpContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
	mpDeviceManager->mpContext->DrawIndexed(mIndexCount, 0, 0);
}
